#include <iostream>
#include <fstream>
#include <string>

#include <stdlib.h>

using namespace std;

#define F_UTF8 1
#define F_1252 2
#define F_8859 4

int test(string& line, int charset){
  int retval = 0;

  int latin, squish, utf8, bad;
  latin = squish = utf8 = bad = 0;
  
  const char* str = line.c_str();
  int len = line.length();

  unsigned char byte;
  for( int i=0; i<len; i++ ){
    byte = str[i];

    if( (byte & 0x80) == 0x00 ){
      latin++;
      continue;
    }


    // Possible 2 BYTE UTF8 Character
    if( (byte & 0xE0) == 0xC0 ){
      if( (len-i) >= 2 ){
        unsigned char byte2 = str[++i];  // Two Byte Character; Process the second byte
        if( (byte2 & 0xC0) == 0x80 ){
          utf8++;
        }else{
				  bad++;
					i--;
        }
      }else{
			  bad++;
      }
      continue;
    }

    // Possible 3 Byte UTF8 Character
    if( (byte & 0xF0) == 0xE0 ){
      if( (len-i) >= 3 ){
        unsigned char byte2 = str[++i];  // Process the second byte
				if( (byte2 & 0xC0) == 0x80 ){
          unsigned char byte3 = str[++i];  // Process the second byte
				  if( (byte3 & 0xC0) == 0x80 ){
					  utf8++;
					}else{
					  bad++;
						i -= 2;
					}
			  }else{
				  bad++;
					i--;
			  }
      }else{
			  bad++;
      }
      continue;
    }

    // Possible 4 Byte UTF-8 Character
		if( (byte & 0xF8) == 0xF0 ){
      if( (len-i) >= 4 ){
			  unsigned char byte2 = str[++i];  // Process the second byte
				if( (byte2 & 0xC0) == 0x80 ){
					unsigned char byte3 = str[++i];  // Process the third byte
					if( (byte3 & 0xC0) == 0x80 ){
						unsigned char byte4 = str[++i];  // Process the fourth byte
						if( (byte4 & 0xC0) == 0x80 ){
						  utf8++;
						}else{
						   bad++;
							 i -= 3;
						}
					}else{
					   bad++;
						 i -= 2;
					}
				}else{
				   bad++;
					 i--;
				}
			}else{
			   bad++;
			}
      continue;
		}
		
    if( (byte & 0xE0) == 0x80 ){
      squish++;
    }else{
      bad++;
    }
  }

  if( charset == F_8859 ){ return bad + utf8; }
  if( charset == F_1252 ){ return squish;     } 
  return utf8;
}

void checkFiles(int charset, char** files, int sz){
  for( int i=0; i<sz; i++ ){
    ifstream is;
    is.open(files[i], ios::binary);
    if( is.is_open() ){
      cout << "Checking [" << files[i] << "]" << endl;
      string ln;
      int l = 1;
      while( !is.eof() ){
        getline(is, ln);
        if( test(ln, charset) ){
          cout << "[" << l << "]: " << ln << endl;
        }
        l++;
      }
    is.close();
    }else{
      cout << "Skipping [" << files[i] << "]: Cannot open file" << endl;
    }
  }
}

void errorMessage(){
  cout << "USAGE: charset -c <charset> file1 [file2 [fileN]]" << endl
       << "       where charset in:" << endl 
       << "         utf8: finds utf8 characters" << endl
       << "         1252: finds Windows-1252 characters" << endl
       << "         8859: finds latin-1 only characters" << endl << endl;
}

int main(int argc, char** argv){
  if( argc == 1 ){
    errorMessage();
    return 0;
  }  

  string arg = argv[1];
  if( arg == "-c" ){
    string charset = argv[2];
    if( charset == "utf8" ){ checkFiles(F_UTF8, (argv+3), argc-3); return 0; }
    if( charset == "1252" ){ checkFiles(F_1252, (argv+3), argc-3); return 0; }
    if( charset == "8859" ){ checkFiles(F_8859, (argv+3), argc-3); return 0; }
    cout << "Error: invalid charset [" << charset << "]" << endl
         << "  Supported charsets: (utf8 | 1252 | 8859)" << endl << endl;
  }else{
    checkFiles(F_UTF8, (argv+1), argc-1);
    return 0;
  }
}
